package org.ljy.lotterysystem.service.impl;
import java.util.*;

import cn.hutool.core.lang.UUID;
import org.ljy.lotterysystem.common.config.DirectRabbitConfig;
import org.ljy.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.ljy.lotterysystem.common.exception.ServiceException;
import org.ljy.lotterysystem.common.utils.JacksonUtil;
import org.ljy.lotterysystem.common.utils.RedisUtil;
import org.ljy.lotterysystem.controller.UserController;
import org.ljy.lotterysystem.controller.param.DrawPrizeParam;
import org.ljy.lotterysystem.controller.param.ShowWinningRecordsParam;
import org.ljy.lotterysystem.dao.dataobject.*;
import org.ljy.lotterysystem.dao.mapper.*;
import org.ljy.lotterysystem.service.IDrawPrizeService;
import org.ljy.lotterysystem.service.dto.WinningRecordDTO;
import org.ljy.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import org.ljy.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.ljy.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.stream.Collectors;

import static org.ljy.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static org.ljy.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements IDrawPrizeService {

    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;


    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IWinningRecordsMapper winningRecordsMapper;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("rabbit消息发送成功,map={}",JacksonUtil.writeValueAsString(map));
    }


    @Override
    public Boolean checkDrawPrizeValid(DrawPrizeParam param) {
        //1.检查活动和奖品是否存在
        ActivityDO activityDO = activityMapper.selectByActivityId(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper
                .selectByAPId(param.getActivityId(),param.getPrizeId());//根据活动id和奖品id进行查询

        if(activityDO == null || activityPrizeDO == null) {
            //throw  new ServiceException(ServiceErrorCodeConstants.DRAW_ACTIVITY_PRIZE_ERROR);
            logger.warn("活动或者奖品不存在,{}", DrawPrizeServiceImpl.class);
            return false;
        }
        //2.检验活动是否有效
        if( !activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.RUNNING.toString())) {
            //throw  new ServiceException(ServiceErrorCodeConstants.ACTIVITY_IS_PASS);
            logger.warn("活动已过期,{}", DrawPrizeServiceImpl.class);
            return false;
        }
        //3.奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.RUNNING.toString())) {
            //throw  new ServiceException(ServiceErrorCodeConstants.PRIZE_IS_PASS);
            logger.warn("奖品已被抽取,{}", DrawPrizeServiceImpl.class);
            return false;
        }
        //4.中奖人数是否 = 奖品数量
        if(param.getWinnerList().size() != activityPrizeDO.getPrizeAmount()) {
            //throw  new ServiceException(ServiceErrorCodeConstants.PRIZE_WINNER_COUNT_ERROR);
            logger.warn("中奖人数和奖品数不对等,{}", DrawPrizeServiceImpl.class);
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordsDO> saveWinnerRecords(DrawPrizeParam param) {
        //保存中奖者名单
        //1.查表寻找数据:活动、人员、奖品、活动奖品关联表
        ActivityDO activityDO = activityMapper.selectByActivityId(param.getActivityId());//活动表
        List<UserDO> userDOList = userMapper.batchSelectByIds( //人员表
                param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));

        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());//奖品表
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        //2.设置保存对象
        List<WinningRecordsDO> winningRecordsDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordsDO winningRecordsDO = new WinningRecordsDO();
                    winningRecordsDO.setActivityId(activityDO.getId());
                    winningRecordsDO.setActivityName(activityDO.getActivityName());
                    winningRecordsDO.setPrizeId(prizeDO.getId());
                    winningRecordsDO.setPrizeName(prizeDO.getName());
                    winningRecordsDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordsDO.setWinnerId(userDO.getId());
                    winningRecordsDO.setWinnerName(userDO.getUserName());
                    winningRecordsDO.setWinnerEmail(userDO.getEmail());
                    winningRecordsDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordsDO.setWinningTime(param.getWinningTime());
                    return winningRecordsDO;
                })
                .collect(Collectors.toList());
        winningRecordsMapper.batchInsert(winningRecordsDOList);
        //3.保存在缓存中
        //(1)保存中奖者信息 -- 奖品维度
        //key + value + timout --- key
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(),
                winningRecordsDOList,
                WINNING_RECORDS_TIMEOUT);
        //(2)保存活动中奖情况 -- 活动维度
        if(!activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.RUNNING.toString())) {
            logger.info("活动维度的中奖情况保存成功");
            List<WinningRecordsDO> allList = winningRecordsMapper.selectByActivity(activityDO.getId());
            cacheWinningRecords(param.getActivityId()+"",
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordsDOList;
    }


    /**
     * 删除中奖记录
     * @param activityId
     * @param prizeId
     */
    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(activityId == null) {
            logger.info("要删除的记录为空");
            return;
        }
        //删除中奖表
        winningRecordsMapper.deleteRecords(activityId, prizeId);
        //删除对应的缓存
        if(prizeId != null) {
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        deleteWinningRecords(String.valueOf(activityId));
    }

    /**
     * 获取中奖记录
     * @param param
     * @return
     */
    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //1.查询缓存
        String key = null == param.getPrizeId()?
                String.valueOf(param.getActivityId()):param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordsDO> winningRecordsDOList = getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winningRecordsDOList)) {
            return convertToWinningRecordDTOList(winningRecordsDOList);
        }
        //2.缓存不存在，查库
        winningRecordsDOList = winningRecordsMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());
        if(CollectionUtils.isEmpty(winningRecordsDOList)) {
            return convertToWinningRecordDTOList(winningRecordsDOList);
        }
        //3.重新放入缓存
        cacheWinningRecords(key, winningRecordsDOList, WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winningRecordsDOList);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinningRecordsDO> winningRecordsDOList) {
        if(CollectionUtils.isEmpty(winningRecordsDOList)) {
            return Arrays.asList();
        }
        return winningRecordsDOList
                .stream()
                .map(winningRecordsDO -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setActivityId(winningRecordsDO.getActivityId());
                    winningRecordDTO.setWinnerName(winningRecordsDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winningRecordsDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordsDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winningRecordsDO.getWinningTime());
                    return winningRecordDTO;
                })
                .collect(Collectors.toList());
    }

    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e) {
            logger.info("缓存删除记录失败");
        }
    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winningRecordsDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordsDO> winningRecordsDOList,
                                     Long time) {
        String str = "";
        try {
            if(!StringUtils.hasText(key) || CollectionUtils.isEmpty(winningRecordsDOList)) {
                logger.warn("要缓存的内容为空");
                return;
            }
            str = JacksonUtil.writeValueAsString(winningRecordsDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX+key,str,time);
        }catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + key, str);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordsDO> getWinningRecords(String key) {
        try {
            if(!StringUtils.hasText(key)) {
                logger.warn("缓存中的数据为空");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX+key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(str,WinningRecordsDO.class);
        }catch (Exception e) {
            logger.error("获取中奖者缓存失败");
            return Arrays.asList();
        }
    }

}
